Перейти к основному содержимому

5.16. Справочник по языку Fortran

Разработчику Архитектору Инженеру

Справочник по языку Fortran

1. Общие характеристики языка

Fortran — компилируемый язык программирования высокого уровня, предназначенный в первую очередь для научных и инженерных вычислений. Язык поддерживает процедурное, модульное и объектно-ориентированное программирование. Fortran отличается строгой типизацией, эффективной работой с массивами, встроенной поддержкой параллелизма и численной стабильностью.

Язык чувствителен к регистру только в строковых литералах и комментариях. В остальном код не зависит от регистра символов. Современные версии Fortran используют свободный формат записи (free-form source), в отличие от фиксированного формата (fixed-form), применяемого в ранних версиях (Fortran 77 и ранее).


2. Структура программы

Программа на Fortran состоит из одной или нескольких программных единиц:

  • Основная программа (program)
  • Внешние подпрограммы (subroutine, function)
  • Модули (module)
  • Блоки внутренних подпрограмм (contains)

Пример минимальной программы:

program hello
implicit none
print *, 'Hello, World!'
end program hello

Ключевое слово implicit none отключает неявные объявления типов и требует явного указания всех переменных.


3. Типы данных

Fortran предоставляет встроенные скалярные и составные типы данных.

3.1. Встроенные скалярные типы

ТипОписаниеПример литерала
integerЦелочисленный тип42, -17
realВещественное число одинарной точности3.14, -2.5e3
double precisionВещественное число двойной точности (устаревший стиль)1.23456789d0
complexКомплексное число(1.0, 2.0)
logicalЛогический тип.true., .false.
characterСимвольная строка'Hello', "Fortran"

3.2. Типы с параметрами (kind)

Каждый встроенный тип может быть дополнен параметром kind, определяющим его представление в памяти и точность.

Примеры:

integer(kind=4) :: i          ! 32-битное целое
integer(kind=8) :: j ! 64-битное целое
real(kind=8) :: x ! double precision
character(len=20) :: name ! строка длиной 20 символов

Стандартные параметры kind можно получить через встроенные функции:

  • selected_int_kind(r) — возвращает kind для целых чисел, способных представлять значения до 10^r
  • selected_real_kind(p, r) — возвращает kind для вещественных чисел с p десятичными цифрами точности и диапазоном 10^±r

Пример:

integer, parameter :: dp = selected_real_kind(15, 307)
real(kind=dp) :: pi = 3.141592653589793_dp

3.3. Производные типы (type)

Пользователь может определять собственные составные типы:

type :: point
real :: x, y
end type point

type(point) :: p1, p2

Производные типы могут содержать компоненты любого типа, включая другие производные типы, массивы и указатели.


4. Переменные и константы

4.1. Объявление переменных

Переменные объявляются с указанием типа и, при необходимости, атрибутов:

integer :: a, b
real, dimension(10) :: vector
character(len=30), parameter :: version = 'Fortran 2018'

4.2. Атрибуты переменных

АтрибутНазначение
parameterКонстанта времени компиляции
dimension(:)Массив (размер указывается в скобках)
allocatableДинамически выделяемый массив
pointerУказатель
targetЦель для указателя
saveСохранение значения между вызовами подпрограммы
intent(in/out/inout)Направление передачи аргумента в подпрограмму
optionalНеобязательный аргумент
public/privateУровень видимости в модуле

4.3. Инициализация

Переменные инициализируются при объявлении или в исполняемом блоке:

integer :: counter = 0
real, dimension(3) :: coords = [1.0, 2.0, 3.0]

Массивы можно инициализировать с помощью конструкторов массивов: [...] или (/ ... /).


5. Операторы

5.1. Арифметические операторы

ОператорОписание
+Сложение
-Вычитание
*Умножение
/Деление
**Возведение в степень

5.2. Логические операторы

ОператорОписание
.and.Логическое И
.or.Логическое ИЛИ
.not.Логическое НЕ
.eqv.Эквивалентность
.neqv.Неэквивалентность

5.3. Операторы сравнения

ОператорАльтернативаОписание
.eq.==Равно
.ne./=Не равно
.lt.<Меньше
.le.<=Меньше или равно
.gt.>Больше
.ge.>=Больше или равно

Современный стиль предпочитает символические формы (==, /=, <, <=, >, >=).


6. Управляющие конструкции

6.1. Условные операторы

if (однострочный):

if (x > 0) print *, 'Positive'

Блоковый if:

if (x < 0) then
print *, 'Negative'
else if (x == 0) then
print *, 'Zero'
else
print *, 'Positive'
end if

6.2. Циклы

Цикл do:

do i = 1, 10
print *, i
end do

Цикл с меткой (устаревший стиль):

do 10 i = 1, 5
print *, i
10 continue

Бесконечный цикл:

do
read *, x
if (x == 0) exit
end do

Управление циклом:

  • exit — выход из цикла
  • cycle — переход к следующей итерации

Циклы могут иметь имена:

outer: do i = 1, 3
inner: do j = 1, 3
if (i == j) cycle outer
print *, i, j
end do inner
end do outer

6.3. Выбор (select case)

select case (status)
case (0)
print *, 'Success'
case (1, 2)
print *, 'Warning'
case default
print *, 'Unknown'
end select

Поддерживается выбор по целым, логическим и символьным значениям.


7. Массивы

Fortran предоставляет мощную встроенную поддержку многомерных массивов.

7.1. Объявление

real, dimension(10) :: a               ! одномерный
real, dimension(5, 5) :: matrix ! двумерный
real, dimension(:,:), allocatable :: dyn_matrix

7.2. Индексация

Индексы по умолчанию начинаются с 1, но можно задать произвольный диапазон:

integer, dimension(-5:5) :: balance

7.3. Операции с массивами

Fortran поддерживает векторизованные операции:

a = b + c        ! поэлементное сложение
d = sin(a) ! поэлементное применение функции
mask = a > 0 ! логический массив

7.4. Срезы (array sections)

vector(2:5)        ! элементы с 2 по 5
matrix(:, 3) ! третий столбец
matrix(1:3, 2:4) ! подматрица

7.5. Динамическое выделение

allocate(dyn_matrix(100, 100))
! ... использование ...
deallocate(dyn_matrix)

Проверка выделения:

if (allocated(dyn_matrix)) deallocate(dyn_matrix)

8. Подпрограммы

Fortran поддерживает два типа подпрограмм: подпрограммы-процедуры (subroutine) и функции (function).

8.1. Подпрограммы (subroutine)

Подпрограмма не возвращает значение напрямую, но может изменять аргументы через параметры с атрибутом intent(out) или intent(inout).

subroutine swap(a, b)
real, intent(inout) :: a, b
real :: temp
temp = a
a = b
b = temp
end subroutine swap

Вызов:

call swap(x, y)

8.2. Функции (function)

Функция возвращает скалярное или массивное значение.

real function distance(x1, y1, x2, y2)
real, intent(in) :: x1, y1, x2, y2
distance = sqrt((x2 - x1)**2 + (y2 - y1)**2)
end function distance

Использование:

d = distance(0.0, 0.0, 3.0, 4.0)

Функции могут быть чистыми (pure) — без побочных эффектов — и элементными (elemental) — автоматически применимыми к массивам.

pure real function square(x)
real, intent(in) :: x
square = x * x
end function square

8.3. Необязательные и ключевые аргументы

Аргументы могут быть помечены как optional. Их наличие проверяется функцией present().

subroutine print_value(x, label)
real, intent(in) :: x
character(len=*), intent(in), optional :: label
if (present(label)) then
print *, trim(label), ': ', x
else
print *, x
end if
end subroutine print_value

Ключевые аргументы позволяют вызывать подпрограмму с именованными параметрами:

call print_value(3.14, label='Pi')

9. Модули (module)

Модули — основной механизм инкапсуляции и повторного использования кода. Они содержат объявления типов, переменных, процедур и интерфейсов.

module math_constants
implicit none
private
public :: pi, e

real, parameter :: pi = 3.141592653589793
real, parameter :: e = 2.718281828459045
end module math_constants

Использование:

program test
use math_constants
print *, 'Pi =', pi
end program test

Модули компилируются отдельно и генерируют .mod-файлы, используемые другими единицами.


10. Объектно-ориентированное программирование

Fortran 2003 ввёл поддержку ООП.

10.1. Производные типы с процедурами

Процедуры могут быть привязаны к типу:

module shape_mod
implicit none

type :: shape
contains
procedure :: area => shape_area
end type shape

contains

real function shape_area(this)
class(shape), intent(in) :: this
shape_area = 0.0
end function shape_area

end module shape_mod

10.2. Наследование

type, extends(shape) :: circle
real :: radius
contains
procedure :: area => circle_area
end type circle

Переопределение метода:

real function circle_area(this)
class(circle), intent(in) :: this
circle_area = pi * this%radius**2
end function circle_area

10.3. Полиморфизм

Переменные могут быть объявлены как class(...), что позволяет хранить объекты разных производных типов.

class(shape), allocatable :: s
allocate(circle :: s)
s%radius = 5.0
print *, s%area()

Для безопасного доступа к компонентам используется select type:

select type (s)
type is (circle)
print *, 'Radius:', s%radius
type is (rectangle)
print *, 'Width:', s%width
end select

11. Ввод и вывод (I/O)

Fortran предоставляет мощные средства форматированного и неформатированного ввода-вывода.

11.1. Основные операторы

  • print * — вывод на стандартный поток
  • write(unit, fmt) — запись в файл или устройство
  • read(unit, fmt) — чтение из файла или устройства

11.2. Файловые операции

Открытие файла:

open(unit=10, file='data.txt', status='old', action='read')

Параметры open:

ПараметрЗначенияОписание
unitцелое числоНомер канала
fileстрокаИмя файла
status'new', 'old', 'replace', 'scratch'Состояние файла
action'read', 'write', 'readwrite'Доступ
form'formatted', 'unformatted'Тип данных
access'sequential', 'direct'Режим доступа
position'rewind', 'append'Позиция

Закрытие:

close(10)

11.3. Форматированный вывод

Формат задаётся строкой:

write(*, '(A, F8.3)') 'Value = ', x

Общие спецификаторы:

  • Iw — целое, ширина w
  • Fw.d — вещественное с фиксированной точкой
  • Ew.d — экспоненциальная форма
  • Aw — строка
  • X — пробел
  • / — новая строка

Пример:

write(*, '(I4, 2X, E12.5)') i, value

11.4. Неформатированный ввод-вывод

Используется для бинарных данных:

open(unit=20, file='binary.dat', form='unformatted')
write(20) array
read(20) array

12. Обработка ошибок

Fortran не имеет исключений в стиле C++ или Python, но поддерживает обработку ошибок через необязательные аргументы iostat, iomsg, stat.

Пример:

integer :: ios
character(len=100) :: errmsg

open(unit=10, file='input.txt', iostat=ios, iomsg=errmsg)
if (ios /= 0) then
print *, 'Ошибка открытия файла: ', trim(errmsg)
stop
end if

Аналогично для allocate:

allocate(big_array(1000000), stat=ios)
if (ios /= 0) then
print *, 'Не хватает памяти'
end if

13. Параллелизм и коарсенная многопоточность

13.1. OpenMP (через директивы)

Fortran широко используется с OpenMP для распараллеливания циклов:

!$omp parallel do
do i = 1, n
a(i) = sin(real(i))
end do
!$omp end parallel do

Требует компиляции с флагом -fopenmp (gfortran) или /Qopenmp (Intel).

13.2. Coarray Fortran (встроенный параллелизм)

Fortran 2008 ввёл коарреи — встроенную поддержку распределённых вычислений.

Объявление:

real, allocatable :: data[:]

Каждый "образ" (image) программы имеет свою копию. Обмен данными:

data[this_image()] = local_value
sync all
total = sum(data)

Запуск: ./program -n 4 (4 образа).


14. Встроенные процедуры (intrinsics)

Fortran содержит сотни встроенных функций и подпрограмм. Ниже — ключевые категории.

14.1. Математические

  • abs(x) — модуль
  • sqrt(x) — квадратный корень
  • sin(x), cos(x), tan(x)
  • exp(x), log(x), log10(x)
  • min(a, b), max(a, b)
  • mod(a, b) — остаток от деления
  • sign(a, b) — перенос знака

14.2. Преобразования типов

  • int(x) — в целое
  • real(x) — в вещественное
  • dble(x) — в double precision
  • cmplx(x, y) — в комплексное

14.3. Работа с массивами

  • size(array[, dim]) — размер
  • shape(array) — форма (массив размерностей)
  • lbound, ubound — границы индексов
  • allocated(array) — выделена ли память
  • merge(mask, true_val, false_val) — условное значение
  • pack(array, mask) — сжатие по маске
  • unpack(vector, mask, field) — распаковка

14.4. Строковые функции

  • len(string) — длина
  • trim(string) — удаление завершающих пробелов
  • adjustl, adjustr — выравнивание
  • index(str, substr) — позиция подстроки
  • char(i) — символ по коду
  • ichar(c) — код символа

14.5. Системные и служебные

  • kind(x) — параметр kind
  • selected_real_kind(p, r)
  • epsilon(x) — машинный эпсилон
  • huge(x) — максимальное значение
  • tiny(x) — минимальное положительное нормализованное
  • date_and_time([values])
  • cpu_time(time) — процессорное время

15. Компиляция и сборка проектов

15.1. Популярные компиляторы

КомпиляторКомандаОсобенности
gfortran (GNU)gfortran -o program main.f90Бесплатный, поддерживает Fortran 2018, часть GCC
ifort / ifx (Intel)ifort -o program main.f90Высокая производительность, оптимизация для Intel CPU
nvfortran (NVIDIA)nvfortran -o program main.f90Поддержка GPU (OpenACC, CUDA Fortran)
flang (LLVM)flang -o program main.f90Современный компилятор на базе LLVM

15.2. Расширения файлов

  • .f — фиксированный формат (Fortran 77 и ранее)
  • .f90 — свободный формат (Fortran 90+)
  • .f95, .f03, .f08 — иногда используются для указания стандарта, но не обязательны
  • .mod — файлы модулей, генерируются автоматически

15.3. Флаги компиляции (gfortran)

ФлагНазначение
-O2, -O3Оптимизация
-gОтладочная информация
-WallВсе предупреждения
-WextraДополнительные предупреждения
-fcheck=allПроверка границ массивов, аргументов и т.п.
-std=f2018Строгое соответствие стандарту
-J<dir>Каталог для .mod-файлов
-I<dir>Каталог для include-файлов
-L<dir> -l<lib>Ссылка на библиотеку

Пример полной команды:

gfortran -O3 -Wall -std=f2018 -J./mod -I./include \
-c utils.f90 -o obj/utils.o

15.4. Сборка с Makefile

Для крупных проектов используется Makefile:

FC = gfortran
FFLAGS = -O2 -Wall -std=f2018 -Jmod -Iinclude
OBJDIR = obj
MODDIR = mod

SRCS = main.f90 math.f90 io.f90
OBJS = $(SRCS:.f90=.o)
OBJS := $(OBJS:%=$(OBJDIR)/%)

$(OBJDIR)/%.o: src/%.f90 | $(MODDIR)
$(FC) $(FFLAGS) -c $< -o $@

$(MODDIR):
mkdir -p $(MODDIR)

program: $(OBJS)
$(FC) $(FFLAGS) $^ -o $@

clean:
rm -rf $(OBJDIR) $(MODDIR) program

16. Отладка и профилирование

16.1. Отладка

  • Используйте -g -fcheck=all для включения проверок.
  • Запуск под gdb:
gfortran -g -fcheck=all -o program main.f90
gdb ./program
  • Внутри gdb: run, break, print, step, continue.

16.2. Профилирование

  • gprof: компиляция с -pg, затем gprof program gmon.out
  • perf (Linux): perf record ./program, perf report
  • Intel VTune, NVIDIA Nsight — для продвинутого анализа

16.3. Статический анализ

  • gcc -fanalyzer (новые версии)
  • Инструменты вроде SonarQube с плагинами для Fortran
  • Ручной код-ревью с акцентом на инициализацию, границы массивов, использование intent

17. Лучшие практики и стиль кода

17.1. Обязательные правила

  • Всегда используйте implicit none.
  • Явно указывайте intent(in), intent(out), intent(inout) для всех аргументов подпрограмм.
  • Инициализируйте все переменные перед использованием.
  • Избегайте глобальных переменных — используйте модули с контролируемой видимостью (private/public).

17.2. Именование

  • Переменные: snake_casetemperature, max_iterations
  • Модули: module_name_modlinear_algebra_mod
  • Типы: type_name_tmatrix_t
  • Константы: UPPER_SNAKE_CASEPI, MAX_BUFFER_SIZE

17.3. Структура файла

!> @brief Краткое описание модуля
!! Подробное описание назначения, зависимостей, примеров использования
module example_mod
implicit none
private

! Публичные типы
type, public :: data_container
real, allocatable :: values(:)
integer :: n = 0
end type data_container

! Публичные процедуры
public :: load_data, process_data

contains

subroutine load_data(...)
! реализация
end subroutine load_data

function process_data(...) result(out)
! реализация
end function process_data

end module example_mod

17.4. Документирование

Хотя Fortran не имеет встроенного механизма документирования, широко применяются комментарии в стиле Doxygen:

!> @brief Вычисляет евклидово расстояние между двумя точками
!! @param x1 Координата X первой точки
!! @param y1 Координата Y первой точки
!! @param x2 Координата X второй точки
!! @param y2 Координата Y второй точки
!! @return Расстояние как вещественное число
real function euclidean_distance(x1, y1, x2, y2)

18. Пример структуры крупного проекта

Согласно современным практикам и типичным шаблонам (включая рекомендации из загруженного файла Структура проекта.txt), проект на Fortran организуется следующим образом:

project/
├── README.md
├── LICENSE
├── Makefile
├── CMakeLists.txt (альтернатива Makefile)
├── src/
│ ├── main.f90
│ ├── core/
│ │ ├── math/
│ │ │ ├── linear_algebra.f90
│ │ │ └── fft.f90
│ │ └── physics/
│ │ ├── fluid_solver.f90
│ │ └── boundary_conditions.f90
│ └── utils/
│ ├── io_utils.f90
│ └── string_utils.f90
├── mod/ (автоматически генерируемые .mod)
├── obj/ (объектные файлы)
├── include/ (если используются .h или .inc)
├── test/
│ ├── test_math.f90
│ └── run_tests.sh
├── examples/
│ └── demo_simulation.f90
└── docs/
└── design_notes.md

Каждый подкаталог src/*/ содержит модули, соответствующие функциональной области. Все модули имеют префикс в имени (например, math_linear_algebra_mod), чтобы избежать конфликтов.


19. Совместимость и переносимость

  • Избегайте расширений конкретного компилятора (например, !DEC$ директив без условной компиляции).
  • Используйте iso_fortran_env для переносимых констант:
use, intrinsic :: iso_fortran_env, only: real64, int32, output_unit
real(real64) :: x
write(output_unit, *) 'Result: ', x
  • Для работы с путями и файлами учитывайте различия ОС (Unix vs Windows), особенно в строках имён файлов.